001 /*
002 * Copyright 2005 Stephen J. McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.station.server;
020
021 import java.util.EventObject;
022 import java.util.EventListener;
023 import java.rmi.server.UnicastRemoteObject;
024 import java.rmi.RemoteException;
025
026 import net.dpml.util.Logger;
027
028
029 /**
030 * A abstract base class that established an event queue and handles event dispatch
031 * operations for listeners declared in a class extending this base class.
032 *
033 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
034 * @version 1.0.1
035 */
036 public abstract class UnicastEventSource extends UnicastRemoteObject
037 {
038 /**
039 * Registered event listeners.
040 */
041 private EventListener[] m_listeners = new EventListener[0];
042
043 private Logger m_logger;
044
045 /**
046 * Internal synchronization lock.
047 */
048 private final Object m_lock = new Object();
049
050 /**
051 * Creation of a new event producer.
052 * @param logger the logging channel
053 * @exception RemoteException if a remote exception occurs
054 */
055 public UnicastEventSource( Logger logger ) throws RemoteException
056 {
057 super();
058
059 m_logger = logger;
060 }
061
062 /**
063 * Return the assigned logging channel.
064 * @return the logging channel
065 */
066 protected Logger getLogger()
067 {
068 return m_logger;
069 }
070
071 /**
072 * Abstract operation to be implemented by classes extending this base class.
073 * An implementation is reposible for the posting of the event to associated
074 * listeners. Event posting will be executed under a separate thread to the
075 * thread that initiated the event post.
076 *
077 * @param event the event to process
078 */
079 protected abstract void processEvent( EventObject event );
080
081 /**
082 * Add a listener to the set of listeners handled by this producer.
083 * @param listener the event listener
084 */
085 protected void addListener( EventListener listener )
086 {
087 if( null == listener )
088 {
089 throw new NullPointerException( "listener" );
090 }
091
092 synchronized( m_lock )
093 {
094 Object[] old = m_listeners;
095 m_listeners = new EventListener[ old.length + 1 ];
096 System.arraycopy( old, 0, m_listeners, 0, old.length );
097 m_listeners[old.length] = listener;
098 }
099 }
100
101 /**
102 * Remove a listener to the set of listeners handled by this producer.
103 * @param listener the event listener
104 */
105 protected void removeListener( EventListener listener )
106 {
107 if( null == listener )
108 {
109 throw new NullPointerException( "listener" );
110 }
111
112 synchronized( m_lock )
113 {
114 if( m_listeners.length == 0 )
115 {
116 throw new IllegalArgumentException( "Listener not registered." );
117 }
118 // create the copy
119 EventListener[] replacement = new EventListener[ m_listeners.length - 1 ];
120 // copy listeners from 0 up to the listener being removed
121 int i=0;
122 while( i < replacement.length && m_listeners[i] != listener )
123 {
124 replacement[i] = m_listeners[i++];
125 }
126 // check that the listener has been located
127 if( i == replacement.length && m_listeners[i] != listener )
128 {
129 throw new IllegalArgumentException( "Listener not registered." );
130 }
131 // complete the copy operation
132 while( i < replacement.length )
133 {
134 replacement[i] = m_listeners[++i];
135 }
136 // commit the copy
137 m_listeners = replacement;
138 }
139 }
140
141 /**
142 * Return this node's preference/node change listeners. Even though
143 * we're using a copy-on-write lists, we use synchronized accessors to
144 * ensure information transmission from the writing thread to the
145 * reading thread.
146 * @return the event listener array
147 */
148 protected EventListener[] listeners()
149 {
150 synchronized( m_lock )
151 {
152 return m_listeners;
153 }
154 }
155
156 /**
157 * Enqueue an event for delivery to registered
158 * listeners unless there are no registered
159 * listeners.
160 * @param event the event to enqueue
161 */
162 protected void enqueueEvent( EventObject event )
163 {
164 if( m_listeners.length != 0 )
165 {
166 RemoteStation.enqueueEvent( event );
167 }
168 }
169 }